package Shifu.MainServer.Net;

import Shifu.MainServer.Management.ChunkDescriptor;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.rmi.server.RemoteServer;
import java.rmi.RMISecurityManager;
import java.rmi.Naming;
import java.io.*;
import java.sql.SQLException;
import Shifu.MainServer.Management.MergedFile;
import Shifu.MainServer.Management.SFileDescriptor;
import Shifu.MainServer.Tools.FileTools;
import Shifu.MainServer.Management.PromotionSet;

/**
 * MobileServer che viene passato nel caso il Login ha buon esito
 * una volta ricevuto dal client, questo si esporta ed è disponibile 
 * per ricevere file.
 */

public class PeerServerImpl implements PeerServer,Serializable,LeecherShellPeerServer,SeederShellPeerServer {

	/* campi */
	
	private Leecher mainserverstub;	//stub per il mainserver
	private LeecherShell shell;	//shell del peerserver
	private final String localDir = "tempfile";   //cartella dove verrano salvati i chunck
	private File localdir = null;
	private PromotionSet ps = null;	//Oggetto per promozione a seeder
	
	/**
	 * Instanzia un PeerServer
	 */

	public PeerServerImpl(Leecher mainstub,LeecherShell shell) throws RemoteException {
		super();
		this.mainserverstub = mainstub;
		this.shell = shell;
	}


	/**
	 * Salva un chunk sul PeerServer
	 * @param cd il descrittore del chunk
	 * @param buffer il chunk
	 * @throws RemoteException se ci sono dei problemi di connessione
 	 */

	public int storeChunk(ChunkDescriptor cd,byte[] buffer) throws RemoteException {
			
		System.out.println("PeerServer: storage del chunk : "+cd.getChunkName() );
		FileOutputStream out = null;

		try {
			
			File file = new File("tempfile/"+cd.getChunkName());
			file.createNewFile();
			out = new FileOutputStream("tempfile/"+file.getName());
			out.write(buffer);	//Scrive il chunk del file
			System.out.println("PeerServer: Controllo MD5 del chunk ricevuto");
			if(!FileTools.check_MD5(file,cd.getHashchunk())) {
					System.out.print("PeerServer: Controllo MD5 fallito\nShifu>");
					return -1;
			}
			

		} catch (IOException e) {
			System.out.print("PeerServer: Errore di IO nella scrittura del file \nShifu>");
			e.printStackTrace();
			return -1;
		}


		finally {

			try {
				out.close(); 
			} catch (IOException e) {
				System.out.print("PeerServer: Errore di IO nella chiusura del file\nShifu>");
				e.printStackTrace();
				return -1;
			}		
		}

		System.out.print("PeerServer: Scrittura del chunk eseguita !\nShifu>");
		return 1;
	}


	/**
	 * Restituisce un chunk voluto
	 * @param cd il descrittore dei chunk che si vuole ottenere
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */

	public byte[] getChunk(ChunkDescriptor cd) throws RemoteException {
		
		System.out.println("PeerServer: invio del chunk : "+cd.getChunkName());
		FileInputStream in = null;
		byte[] buffer = null;

		try {
			in = new FileInputStream("tempfile/"+cd.getChunkName());
			int dimbuff = (int) cd.getDimChunk();
			buffer = new byte[dimbuff];	//crea un buffer della dimensione del chunk
			in.read(buffer);			//Legge dal chunk						
			
		}catch (IOException e) {
			System.out.println("PeerServer: Errore di IO nella lettura del chunk \nShifu>");
			return null;
		}
	
		finally {
		
			/*se lo stream non a null */
			if(in != null) {
		
				try {
					in.close();
				} catch (IOException e) {
					System.out.println("PeerServer: Errore di IO nella chiusura del file\nShifu>");
					return null;
				}
			}			
		}

		System.out.print("PeerServer: Invio del chunk eseguito !\nShifu>");	
		return buffer;
	}
		

	/**
	 * Rimuove un determinato chunk del PeerServer
	 * @param cd il descrittore del chunk che si vuole eliminare
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */

	public int removeChunk(ChunkDescriptor cd) throws RemoteException {
			
		System.out.println("PeerServer: Rimozione del chunk : "+cd.getChunkName());		

		try {

			File file = new File("tempfile/"+cd.getChunkName());	
			file.delete();
			System.out.print("PeerServer: Chunk Rimosso\nShifu>");		   	
						
		} catch (Exception e) {
		   	System.out.print("PeerServer: Errore chunk non presente !!\nShifu>");
			return -1;
		}	

		return 1;
	}

	/**
	 * Risponde se il PeerServer è ancora alive
	 * @return 0 se è alive
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */

	public int areYouAlive() throws RemoteException {
		return 0;
	}

	/**
	 * Esegue il download di un determinato file
	 * @param i indice del file da scaricare
	 * @return 1 se va a buon fine, -1 altrimenti
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */

	public int download(int i) throws RemoteException {

		System.out.println("PeerServer: invio richiesta download del file:" +i+" al MainServer !");
		MergedFile mf = mainserverstub.getFile(i);
		SFileDescriptor sfd;
		FileOutputStream out = null;

		if(mf == null)  return -1;		
	
		try {
			sfd = mf.getSfd();
			File file = new File(sfd.getFileName());
			file.createNewFile();
			out = new FileOutputStream(file.getName());
			byte[] buffer = mf.getFile();
			out.write(buffer);	//Scrive il contenuto del file
			System.out.println("PeerServer: Controllo MD5 del file ricevuto");
			if(!FileTools.check_MD5(file,sfd.getHash())) {
					System.out.println("PeerServer: Controllo MD5 fallito");
					return -1;
			}
						
		} catch (IOException ioe) {
			System.out.println("PeerServer: Errore di IO nella ricezione dei file");
			return -1;
		}

		finally {
			
			try {
				out.close();
			} catch (IOException io) {
				System.out.println("PeerServer: Errore nella chiusura del file");		
			}
		}

		System.out.println("PeerServer: Download del file eseguito, il file: "+sfd.getFileName()+" è presente nella cartella");
		return 1;
	}

	/**
	 * Richiede la lista dei file nel sistema
	 * @return la lista dei file
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */

	public String fileList() throws RemoteException {
		
		System.out.println("PeerServer: invio richiesta elenco file al MainServer !");
		String filelist = mainserverstub.getFileList();
		return filelist;
	}

	/**
	 * Richiede la lista dei peers
	 * @return la lista dei peers
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */

	public String peerList() throws RemoteException {
		
		System.out.println("PeerServer: invio richiesta elenco peer al MainServer !");
		String peerlist = mainserverstub.getPeerList();
		return peerlist;
	}

	/**
	 * Deesporta il PeerServer
	 * @return 1 se va a buon fine, -1 altrimenti
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */

	public int exit() throws RemoteException {
		
		System.out.println("PeerServer: Deesportazione del peerserver");
		UnicastRemoteObject.unexportObject(this,false);
		System.out.println("PeerServer: Eliminazione cartella tempfile");
		boolean succ = dirDelate(localdir);
		if(succ)
			System.out.println("PeerServer: Cartella tempfile cancellata");
		else
			System.out.println("PeerServer: Fallita la cancellazione della cartella tempfile!");

		return 1;
	}

	/**
	 * Esegue l'upload di un determinato file
	 * @param filename nome del file da inserire nel sistema
	 * @return 1 se va a buon fine, -1 altrimenti
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */

	public int upload(String filename) throws RemoteException {
		
		System.out.println("PeerServer: upload del file: "+filename+" al mainserver");
		FileInputStream in = null;

		try {
			File file = new File(filename);
			in = new FileInputStream(filename);
			int filedim = (int) file.length();
			byte[] buffer = new byte[filedim];			
			in.read(buffer);	//Legge il file
			SFileDescriptor sfd = new SFileDescriptor(filename,Shifu.MainServer.Tools.FileTools.generate_MD5(file));	//crea il descrittore del file
			Seeder stubcast = (Seeder) mainserverstub;//promuove lo stub a Seeder
			return (stubcast.storageFile(sfd,buffer));		

		} catch (FileNotFoundException io) {
			System.out.println("PeerServer: Errore file: "+filename+" non trovato !");
			return -1;
		} catch (IOException io) {
			System.out.println("PeerServer: Errore di IO nel file: "+filename+" !");
			return -1;	
		}

	}

	/**
	 * Rimuove un file dal sistema
	 * @param i indice del file da rimuovere
	 * @return 1 se va a buon fine, -1 altrimenti
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */

	public int removeFile(int i) throws RemoteException {

		System.out.println("PeerServer: richiesta cancellazione del file di indice: "+i+" al mainserver");
		Seeder caststub = (Seeder) mainserverstub;	//promuove lo stub a Seeder
		return (caststub.removeFile(i));

	}

	/**
	 * Esegue la promozione di un peer
	 * @param i indice del peer da promuovere
	 * @return 1 se va a buon fine, -1 altrimenti
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */

	public int promovePeer(int i) throws RemoteException {
	
		System.out.println("PeerServer: richiesta promozione del peer di indice: "+i+" al mainserver");	
		Seeder caststub = (Seeder) mainserverstub;	//promuove lo stub a Seeder
		return (caststub.promovePeer(i));
	}

	/**
	 * Esegue la shell
	 */
	
	public void executeShell() {

		System.out.println("AVVIO DELLA SHELL");
		shell.setPeerServerStub(this);	//Mette il proprio stub nella shell
		this.shell.start();
	}

	 private static boolean dirDelate(File dir) {

		if(dir == null) return false;

	    	if (dir.isDirectory()) {
	      		String[] filelist = dir.list();
	      		for (int i=0; i<filelist.length; i++) {
				boolean success = dirDelate(new File(dir, filelist[i]));
				if (!success) return false; 
	      		}
	    	}
	    	return dir.delete();
	  }

	/**
	 * Registra il peerserver sul MainServer
         * @param username username del peer
	 * @return 1 se va a buon fine -1 altrimenti
         */

	public int registrate(String username) throws RemoteException {

		System.out.println("PeerServer: invio richiesta di registrazione di "+username+" al mainserver");
		System.out.println("PeerServer: Creazione delle cartelle per file");
		localdir = new File(localDir);
		boolean val = localdir.mkdir();
		if (val)
			System.out.println("PeerServer: Cartella tempfile creata");
		else 
			System.out.println("PeerServer: Errore nella creazione della cartella tempfile!");

		return mainserverstub.add(this,username);		
	}

	/**
	 * Riceve la promozione 
	 * @param ps oggetto con gli stumenti per la promozione
	 * @return 1 se va a buon fine, -1 altrimenti
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */


	public int getPromotion(PromotionSet ps) throws RemoteException {
		
		System.out.print("\nPeerServer: SEI STATO PROMOSSO A LIVELLO SEEDER !!! Per aggiornare la Shell digita il comando upgrade!\nShifu>");
		this.ps = ps;
		return 1;	
	}

	/**
	 * Esegue l'upgrade della shell
	 * @throws RemoteException se ci sono dei problemi di connessione
	 */

	public void upgrade() throws RemoteException {

		if(this.ps != null) {

			System.out.println("\nPeerServer: Aggiornamento in corso!\nOra la shell verra fatta terminare e verranno aggiornati gli stub!");
			this.mainserverstub = this.ps.getSeederStub();
			System.out.println("PeerServer: Stub aggiornato!\nCaricamento nuova shell in corso....");
			this.shell = this.ps.getSeederShell();
			shell.setPeerServerStub(this);
			System.out.println("PeerServer: Aggiornamento termianto, avvio della nuova shell.");
			this.shell.start();

		} else {
			System.out.println("PeerServer: Impossibile eseguire upgrade, non hai ricevuto nessuna promozione");
		}
	}
}
